/*
 *
 * Created on: 15.07.2012
 *
 * DHLogAnalyzer.cpp
 * This file is part of KOBUS
 * 
 * Copyright (C) 2012 - Ivan Penkin, MIPT
 * grek.penkin@gmail.com
 *
 * KOBUS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * KOBUS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with KOBUS; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, 
 * Boston, MA  02110-1301  USA
 *  
 */

#include <KobusCommon.h>
#include <DHLogAnalyzer.h>

using namespace kobus;
using namespace dhcp;


DHLogAnalyzer::DHLogAnalyzer(QObject * parent)
    :QObject(parent),
     tail(new QProcess(/*this*/)),
     coreThread(new QThread(this)),
     core(new TCPCoreClient()) {

  message.reserve(1024);

  QObject::connect(tail, SIGNAL(readyReadStandardOutput()),
                this, SLOT(processTailMessage()));

  QObject::connect(QDaemonApplication::instance(), SIGNAL(termSignal()),
                this, SLOT(terminate()));
  QObject::connect(core, SIGNAL(error(QString)),
                this, SLOT(terminate()), Qt::QueuedConnection);
  QObject::connect(tail, SIGNAL(error(QProcess::ProcessError)),
                this, SLOT(onError()));
}


DHLogAnalyzer::~DHLogAnalyzer() {

  if (tail -> state() == QProcess::Running) {
    tail -> terminate();
  }

  tail -> waitForFinished(500);
}


void DHLogAnalyzer::terminate() {

  if (core -> isOpen())
    core -> disconnectFromHost();
  QDaemonApplication::instance() -> exit(0);
}


void DHLogAnalyzer::onError() {

  Log log;

  log.error("Error occurred with log analyzer process. Stopping...");
  terminate();
}

void DHLogAnalyzer::start() {

  core -> moveToThread(coreThread);
  coreThread -> start();
  core -> connectToCore();

  Log     log;
  QString ifaces;

  try {

    ifaces =  config.getString(DHConfig::Keys::dhcpLog::allowedIfaces);

    acknowledgementParser.initializeByConfig();
    releaseParser.initializeByConfig();

  } catch (Exception e) {
    terminate();
  }

  foreach(QString iface,
            ifaces.split(QRegExp("(,\\s)|(,)"), QString::SkipEmptyParts)) {
    interfaces.push_back(iface);
  }

  startTail();
}


void DHLogAnalyzer::startTail() {

  tail -> blockSignals(true);

  Log         log;
  QStringList tailParams;

  log.debug(Log::FACILE, "Preparing to start tail process.");

  tailParams.push_back("-F");
  tailParams.push_back("-q");
  tailParams.push_back("-n0");
  try {
    tailParams.push_back(config.getString(DHConfig::Keys::dhcpLog::logPath));
  } catch (Exception e) {
    terminate();
  }

  tail -> start("/usr/bin/tail", tailParams);
  tail -> waitForStarted(1500);

  if (tail -> state() != QProcess::Running) {
    log.error("Couldn't start tail process.");
    terminate();
  }
  tail -> setReadChannel(QProcess::StandardOutput);
  tail -> blockSignals(false);
}


void DHLogAnalyzer::processTailMessage() {

  Log log;
  tail -> blockSignals(true);

  log.debug(Log::FACILE, "DHLogAnalyzer::processTailMessage()");

  while (tail -> canReadLine()) {
    message.clear();
    message.append(tail -> readLine(1024));

    log.debug(Log::FACILE, "Received: " + message);

    //TODO: check interface name
    if (message.contains("DHCPACK", Qt::CaseInsensitive)) {
      processDhcpAck();
    } else if (message.contains("DHCPRELEASE", Qt::CaseInsensitive)) {
      processDhcpRelease();
    }
  };

  tail -> blockSignals(false);
}


void DHLogAnalyzer::processDhcpAck() {

  Log log;
  log.debug(Log::FACILE, "DHLogAnalyzer::processDhcpAck()");

  HostInfo info;

  try {
    acknowledgementParser.setMessage(message);
    acknowledgementParser.parse();
    info = acknowledgementParser.getNewHostInfo();
    if (!interfaces.contains(info.getIface())) {
      throw Exception(Exception::NO_ERRNO,
          "Interface '" + info.getIface() + "' doesn't managed.");
    }
  } catch (Exception e) {
    log.warning("Skipping the message...");
    return;
  }

  core -> sendLogItem(info);
}


void DHLogAnalyzer::processDhcpRelease() {

  Log log;
  log.debug(Log::FACILE, "DHLogAnalyzer::processDhcpRelease()");

  HostInfo info;

  try {
    releaseParser.setMessage(message);
    releaseParser.parse();
    info = releaseParser.getNewHostInfo();

  } catch (Exception e) {
    log.warning("Skipping the message...");
    return;
  }

   core -> sendLogItem(info);
}
